Aaron.register('Emitter',  function() {

    function Emitter(ctx) {
        this._ctx = ctx || this
    }

    var EmitterProto = Emitter.prototype

    EmitterProto.on = function(event, fn) {
        this._cbs = this._cbs || {};
        (this._cbs[event] = this._cbs[event] || [])
            .push(fn)
        return this
    }

    EmitterProto.once = function(event, fn) {
        var self = this
        this._cbs = this._cbs || {}

            function on() {
                self.off(event, on)
                fn.apply(this, arguments)
            }

        on.fn = fn
        this.on(event, on)
        return this
    }

    EmitterProto.off = function(event, fn) {
        this._cbs = this._cbs || {}

        // all
        if (!arguments.length) {
            this._cbs = {}
            return this
        }

        // specific event
        var callbacks = this._cbs[event]
        if (!callbacks) return this

        // remove all handlers
        if (arguments.length === 1) {
            delete this._cbs[event]
            return this
        }

        // remove specific handler
        var cb
        for (var i = 0; i < callbacks.length; i++) {
            cb = callbacks[i]
            if (cb === fn || cb.fn === fn) {
                callbacks.splice(i, 1)
                break
            }
        }
        return this
    }

    EmitterProto.emit = function(event, a, b, c) {
        this._cbs = this._cbs || {}
        var callbacks = this._cbs[event]

        if (callbacks) {
            callbacks = callbacks.slice(0)
            for (var i = 0, len = callbacks.length; i < len; i++) {
                callbacks[i].call(this._ctx, a, b, c)
            }
        }

        return this
    }

    return Emitter
})